#include "sdk.h"
#include "compilercommandgenerator.h"
#include <wx/arrimpl.cpp>
#include <wx/filefn.h>
#include <wx/xml/xml.h>
// TODO (m#1#2019年11月09日): 修复编译器 $file_name 宏错误

wxArrayString Compiler::m_CompilerIDs;
const wxString Compiler::FilePathWithSpaces = _T( "[][{}() \t#%$~[:alnum:]&_:+/\\.-]+" );
const wxString CompilerSettingsVersion = _T( "0.0.3" );
const wxString EmptyString;

CompilerSwitches::CompilerSwitches() {
  includeDirs = _T( "-I" );
  libDirs = _T( "-L" );
  linkLibs = _T( "-l" );
  defines = _T( "-D" );
  genericSwitch = _T( "-" );
  objectExtension = _T( "o" );
  needDependencies = true;
  forceFwdSlashes = false;
  forceCompilerUseQuotes = false;
  forceLinkerUseQuotes = false;
  logging = defaultLogging;
  libPrefix = _T( "lib" );
  libExtension = _T( "a" );
  linkerNeedsLibPrefix = false;
  linkerNeedsLibExtension = false;
  linkerNeedsPathResolved = false;
  supportsPCH = true;
  PCHExtension = _T( "gch" );
  UseFlatObjects = false;
  UseFullSourcePaths = false;
  includeDirSeparator = _T( ' ' );
  libDirSeparator = _T( ' ' );
  objectSeparator = _T( ' ' );
  statusSuccess  = 0;
}

wxString Compiler::CommandTypeDescriptions[ctCount] = {
  _T( "Compile_目标文件" ),
  _( "Compile_依赖关系" ),
  _T( "Compile Win32 resource file" ),
  _( "Link_可执行文件" ),
  _( "Link_控制台" ),
  _( "Link_动态库" ),
  _T( "Link_静态库" ),
  _T( "Link_驱动程序" )
};

Compiler::Compiler( const wxString& name, const wxString& ID, const wxString& parentID, int weight ) :
  m_Name( name ),
  m_MultiLineMessages( false ),
  m_ID( ID.Lower() ),
  m_ParentID( parentID.Lower() ),
  m_Valid( false ),
  m_NeedValidityCheck( true ),
  m_Mirrored( false ) {
  MakeValidID();
  m_Switches.supportsPCH = false;
  m_Switches.forceFwdSlashes = false;
  m_VersionString = wxEmptyString;
  m_Weight = weight;
  m_RegExes.reserve( 100 );
  Manager::Get()->GetLogManager()->Log( F( _T( "加载编译器 \"%s\"" ), m_Name.wx_str() ) );
}

Compiler::Compiler( const Compiler& other ) :
  CompileOptionsBase( other ), m_ParentID( other.m_ParentID.IsEmpty() ? other.m_ID : other.m_ParentID ), m_Mirror( other.m_Mirror ), m_Mirrored( other.m_Mirrored ) {
  m_Name = _T( "Copy of " ) + other.m_Name;
  m_MultiLineMessages = other.m_MultiLineMessages;
  wxDateTime now = wxDateTime::UNow();
  m_ID = now.Format( _T( "%c" ), wxDateTime::CET );
  MakeValidID();
  m_MasterPath = other.m_MasterPath;
  m_ExtraPaths = other.m_ExtraPaths;
  m_Programs = other.m_Programs;
  m_Switches = other.m_Switches;
  m_Options = other.m_Options;
  m_SortOptions[0]  = other.m_SortOptions[0];
  m_SortOptions[1]  = other.m_SortOptions[1];
  m_IncludeDirs = MakeUniqueArray( other.m_IncludeDirs, true );
  m_LibDirs = MakeUniqueArray( other.m_LibDirs,  true );
  m_CompilerOptions = other.m_CompilerOptions;
  m_LinkerOptions = other.m_LinkerOptions;
  m_LinkLibs = other.m_LinkLibs;
  m_CmdsBefore = other.m_CmdsBefore;
  m_CmdsAfter = other.m_CmdsAfter;
  m_RegExes = other.m_RegExes;
  m_VersionString = other.m_VersionString;
  m_Weight = 100;
  for( int i = 0; i < ctCount; ++i ) {
    m_Commands[( CommandType )i] = other.m_Commands[( CommandType )i];
  }
  m_Valid = other.m_Valid;
  m_NeedValidityCheck = other.m_NeedValidityCheck;
}

Compiler::~Compiler() {
}

void Compiler::Reset() {
  m_Options.ClearOptions();
  for( int i = 0; i < ctCount; ++i ) {
    m_Commands[i].clear();
  }
  LoadDefaultOptions( GetID() );
  LoadDefaultRegExArray();
  m_CompilerOptions.Clear();
  m_LinkerOptions.Clear();
  m_LinkLibs.Clear();
  m_CmdsBefore.Clear();
  m_CmdsAfter.Clear();
}

void Compiler::ReloadOptions() {
  if( ConfigManager::LocateDataFile( _T( "compilers/options_" ) + GetID() + _T( ".xml" ), sdDataUser | sdDataGlobal ).IsEmpty() ) {
    return;
  }
  m_Options.ClearOptions();
  LoadDefaultOptions( GetID() );
  LoadDefaultRegExArray();
}

void Compiler::LoadDefaultRegExArray( bool globalPrecedence ) {
  m_RegExes.clear();
  LoadRegExArray( GetID(), globalPrecedence );
}


bool Compiler::IsValid() {
  if( !m_NeedValidityCheck ) {
    return m_Valid;
  }
  if( m_MasterPath.IsEmpty() ) {
    return true;
  }
  m_NeedValidityCheck = false;
  wxString tmp = m_MasterPath + _T( "/bin/" ) + m_Programs.C;
  MacrosManager *macros = Manager::Get()->GetMacrosManager();
  macros->ReplaceMacros( tmp );
  m_Valid = wxFileExists( tmp );
  if( !m_Valid ) {
    tmp = m_MasterPath + _T( '/' ) + m_Programs.C;
    macros->ReplaceMacros( tmp );
    m_Valid = wxFileExists( tmp );
  }
  if( !m_Valid ) {
    for( size_t i = 0; i < m_ExtraPaths.GetCount(); ++i ) {
      tmp = m_ExtraPaths[i] + _T( '/' ) + m_Programs.C;
      macros->ReplaceMacros( tmp );
      m_Valid = wxFileExists( tmp );
      if( m_Valid ) {
        break;
      }
    }
  }
  return m_Valid;
}


wxString Compiler::MakeInvalidCompilerMessages() const {
  MacrosManager *macros = Manager::Get()->GetMacrosManager();
  wxString triedPathsMsgs;
  wxString tmp = m_MasterPath + _T( "/bin/" ) + m_Programs.C;
  macros->ReplaceMacros( tmp );
  triedPathsMsgs += F( _T( "尝试运行编译器可执行文件 '%s', 但是失败了!\n" ), tmp.wx_str() );
  tmp = m_MasterPath + _T( "/" ) + m_Programs.C;
  macros->ReplaceMacros( tmp );
  for( size_t i = 0; i < m_ExtraPaths.GetCount(); ++i ) {
    triedPathsMsgs += F( _T( "尝试运行编译器可执行文件 '%s', 但是失败了!\n" ), tmp.wx_str() );
    tmp = m_ExtraPaths[i] + _T( "/" ) + m_Programs.C;
    macros->ReplaceMacros( tmp );
  }
  return triedPathsMsgs;
}

void Compiler::MakeValidID() {
  wxString newID;
  if( m_ID.IsEmpty() ) {
    m_ID = m_Name;
  }
  size_t pos = 0;
  while( pos < m_ID.Length() ) {
    wxChar ch = m_ID[pos];
    if( wxIsalnum( ch ) || ch == _T( '_' ) || ch == _T( '-' ) ) {
      newID.Append( ch );
    } else if( wxIsspace( ch ) ) {
      newID.Append( _T( '_' ) );
    }
    ++pos;
  }
  if( wxIsdigit( newID.GetChar( 0 ) ) || newID.GetChar( 0 ) == _T( '-' ) ) {
    newID.Prepend( _T( "cb" ) );
  }
  if( newID.IsEmpty() ) {
    m_Throw( _T( "无法创建有效的编译器ID " ) + m_Name );
  }
  m_ID = newID.Lower();
  if( !IsUniqueID( m_ID ) ) {
    m_Throw( _T( "已经存在编译器ID " ) + m_Name );
  }
  m_CompilerIDs.Add( m_ID );
}

CompilerCommandGenerator* Compiler::GetCommandGenerator( M_Code_Project* project ) {
  CompilerCommandGenerator* generator = new CompilerCommandGenerator;
  generator->Init( project );
  return generator;
}

const wxString& Compiler::GetCommand( CommandType ct, const wxString& fileExtension ) const {
  const CompilerToolsVector& vec = m_Commands[ct];
  if( vec.empty() ) {
    return EmptyString;
  }
  size_t catchAll = 0;
  if( !fileExtension.IsEmpty() ) {
    for( size_t i = 0; i < vec.size(); ++i ) {
      if( vec[i].extensions.GetCount() == 0 ) {
        catchAll = i;
        continue;
      }
      for( size_t n = 0; n < vec[i].extensions.GetCount(); ++n ) {
        if( vec[i].extensions[n] == fileExtension ) {
          return vec[i].command;
        }
      }
    }
  }
  return vec[catchAll].command;
}

const CompilerTool* Compiler::GetCompilerTool( CommandType ct, const wxString& fileExtension ) const {
  const CompilerToolsVector& vec = m_Commands[ct];
  if( vec.empty() ) {
    return nullptr;
  }
  size_t catchAll = 0;
  if( !fileExtension.IsEmpty() ) {
    for( size_t i = 0; i < vec.size(); ++i ) {
      if( vec[i].extensions.GetCount() == 0 ) {
        catchAll = i;
        continue;
      }
      for( size_t n = 0; n < vec[i].extensions.GetCount(); ++n ) {
        if( vec[i].extensions[n] == fileExtension ) {
          return &vec[i];
        }
      }
    }
  }
  return &vec[catchAll];
}

void Compiler::MirrorCurrentSettings() {
  if( m_Mirrored ) {
    return;
  }
  m_Mirror.Name   = m_Name;
  m_Mirror.MasterPath  = m_MasterPath;
  m_Mirror.ExtraPaths  = m_ExtraPaths;
  for( int i = 0; i < ctCount; ++i ) {
    m_Mirror.Commands[i]  = m_Commands[i];
  }
  m_Mirror.Programs = m_Programs;
  m_Mirror.Switches = m_Switches;
  m_Mirror.Options = m_Options;
  m_Mirror.RegExes = m_RegExes;
  m_Mirror.CompilerOptions_ = m_CompilerOptions;
  m_Mirror.LinkerOptions = m_LinkerOptions;
  m_Mirror.IncludeDirs = MakeUniqueArray( m_IncludeDirs, true );
  m_Mirror.ResIncludeDirs   = MakeUniqueArray( m_ResIncludeDirs, true );
  m_Mirror.LibDirs = MakeUniqueArray( m_LibDirs,  true );
  m_Mirror.LinkLibs = m_LinkLibs;
  m_Mirror.CmdsBefore  = m_CmdsBefore;
  m_Mirror.CmdsAfter   = m_CmdsAfter;
  m_Mirror.SortOptions[0] = m_SortOptions[0];
  m_Mirror.SortOptions[1] = m_SortOptions[1];
  m_Mirrored  = true;
}

void Compiler::SaveSettings( const wxString& baseKey ) {
  ConfigManager* cfg = Manager::Get()->GetConfigManager( _T( "compiler" ) );
  wxString tmp;
  tmp.Printf( _T( "%s/set%3.3d" ), baseKey.c_str(), CompilerFactory::GetCompilerIndex( this ) + 1 );
  cfg->DeleteSubPath( tmp );
  tmp.Printf( _T( "%s/%s" ), baseKey.c_str(), m_ID.c_str() );
  cfg->Write( tmp + _T( "/name" ), m_Name );
  cfg->Write( tmp + _T( "/parent" ), m_ParentID, true );
  if( m_Mirror.CompilerOptions_ != m_CompilerOptions ) {
    wxString key = GetStringFromArray( m_CompilerOptions );
    cfg->Write( tmp + _T( "/compiler_options" ), key, false );
  }
  if( m_Mirror.ResourceCompilerOptions != m_ResourceCompilerOptions ) {
    wxString key = GetStringFromArray( m_ResourceCompilerOptions );
    cfg->Write( tmp + _T( "/resource_compiler_options" ), key, false );
  }
  if( m_Mirror.LinkerOptions != m_LinkerOptions ) {
    wxString key = GetStringFromArray( m_LinkerOptions );
    cfg->Write( tmp + _T( "/linker_options" ), key, false );
  }
  if( m_Mirror.IncludeDirs != m_IncludeDirs ) {
    wxString key = GetStringFromArray( MakeUniqueArray( m_IncludeDirs, true ) );
    cfg->Write( tmp + _T( "/include_dirs" ), key, false );
  }
  if( m_Mirror.ResIncludeDirs != m_ResIncludeDirs ) {
    wxString key = GetStringFromArray( MakeUniqueArray( m_ResIncludeDirs, true ) );
    cfg->Write( tmp + _T( "/res_include_dirs" ), key, false );
  }
  if( m_Mirror.LibDirs != m_LibDirs ) {
    wxString key = GetStringFromArray( MakeUniqueArray( m_LibDirs, true ) );
    cfg->Write( tmp + _T( "/library_dirs" ), key, false );
  }
  if( m_Mirror.LinkLibs != m_LinkLibs ) {
    wxString key = GetStringFromArray( m_LinkLibs );
    cfg->Write( tmp + _T( "/libraries" ),  key, false );
  }
  if( m_Mirror.CmdsBefore != m_CmdsBefore ) {
    wxString key = GetStringFromArray( m_CmdsBefore );
    cfg->Write( tmp + _T( "/commands_before" ), key, true );
  }
  if( m_Mirror.CmdsAfter != m_CmdsAfter ) {
    wxString key = GetStringFromArray( m_CmdsAfter );
    cfg->Write( tmp + _T( "/commands_after" ), key, true );
  }
  if( m_Mirror.MasterPath != m_MasterPath ) {
    cfg->Write( tmp + _T( "/master_path" ), m_MasterPath, true );
  }
  if( m_Mirror.ExtraPaths != m_ExtraPaths ) {
    cfg->Write( tmp + _T( "/extra_paths" ), GetStringFromArray( MakeUniqueArray( m_ExtraPaths, true ), _T( ";" ) ), true );
  }
  if( m_Mirror.Programs.C != m_Programs.C ) {
    cfg->Write( tmp + _T( "/c_compiler" ), m_Programs.C, true );
  }
  if( m_Mirror.Programs.CPP != m_Programs.CPP ) {
    cfg->Write( tmp + _T( "/cpp_compiler" ), m_Programs.CPP, true );
  }
  if( m_Mirror.Programs.LD != m_Programs.LD ) {
    cfg->Write( tmp + _T( "/linker" ), m_Programs.LD,  true );
  }
  if( m_Mirror.Programs.LIB != m_Programs.LIB ) {
    cfg->Write( tmp + _T( "/lib_linker" ), m_Programs.LIB,  true );
  }
  if( m_Mirror.Programs.MAKE != m_Programs.MAKE ) {
    cfg->Write( tmp + _T( "/make" ), m_Programs.MAKE, true );
  }
  if( m_Mirror.Programs.DBGconfig != m_Programs.DBGconfig ) {
    cfg->Write( tmp + _T( "/debugger_config" ), m_Programs.DBGconfig, true );
  }
  for( int i = 0; i < ctCount; ++i ) {
    for( size_t n = 0; n < m_Commands[i].size(); ++n ) {
      if( n >= m_Mirror.Commands[i].size() || m_Mirror.Commands[i][n] != m_Commands[i][n] ) {
        wxString key = F( _T( "%s/macros/%s/tool%lu/" ), tmp.c_str(), CommandTypeDescriptions[i].c_str(), static_cast<unsigned long>( n ) );
        cfg->Write( key + _T( "command" ), m_Commands[i][n].command );
        cfg->Write( key + _T( "extensions" ), m_Commands[i][n].extensions );
        cfg->Write( key + _T( "generatedFiles" ), m_Commands[i][n].generatedFiles );
      }
    }
  }
  if( m_Mirror.Switches.includeDirs != m_Switches.includeDirs ) {
    cfg->Write( tmp + _T( "/switches/includes" ), m_Switches.includeDirs, true );
  }
  if( m_Mirror.Switches.libDirs != m_Switches.libDirs ) {
    cfg->Write( tmp + _T( "/switches/libs" ), m_Switches.libDirs, true );
  }
  if( m_Mirror.Switches.linkLibs != m_Switches.linkLibs ) {
    cfg->Write( tmp + _T( "/switches/link" ), m_Switches.linkLibs, true );
  }
  if( m_Mirror.Switches.defines != m_Switches.defines ) {
    cfg->Write( tmp + _T( "/switches/define" ), m_Switches.defines, true );
  }
  if( m_Mirror.Switches.genericSwitch != m_Switches.genericSwitch ) {
    cfg->Write( tmp + _T( "/switches/generic" ), m_Switches.genericSwitch, true );
  }
  if( m_Mirror.Switches.objectExtension != m_Switches.objectExtension ) {
    cfg->Write( tmp + _T( "/switches/objectext" ), m_Switches.objectExtension, true );
  }
  if( m_Mirror.Switches.needDependencies != m_Switches.needDependencies ) {
    cfg->Write( tmp + _T( "/switches/deps" ), m_Switches.needDependencies );
  }
  if( m_Mirror.Switches.forceCompilerUseQuotes != m_Switches.forceCompilerUseQuotes ) {
    cfg->Write( tmp + _T( "/switches/forceCompilerQuotes" ), m_Switches.forceCompilerUseQuotes );
  }
  if( m_Mirror.Switches.forceLinkerUseQuotes != m_Switches.forceLinkerUseQuotes ) {
    cfg->Write( tmp + _T( "/switches/forceLinkerQuotes" ), m_Switches.forceLinkerUseQuotes );
  }
  if( m_Mirror.Switches.logging != m_Switches.logging ) {
    cfg->Write( tmp + _T( "/switches/logging" ),  m_Switches.logging );
  }
  if( m_Mirror.Switches.libPrefix != m_Switches.libPrefix ) {
    cfg->Write( tmp + _T( "/switches/libPrefix" ), m_Switches.libPrefix, true );
  }
  if( m_Mirror.Switches.libExtension != m_Switches.libExtension ) {
    cfg->Write( tmp + _T( "/switches/libExtension" ), m_Switches.libExtension, true );
  }
  if( m_Mirror.Switches.linkerNeedsLibPrefix != m_Switches.linkerNeedsLibPrefix ) {
    cfg->Write( tmp + _T( "/switches/linkerNeedsLibPrefix" ), m_Switches.linkerNeedsLibPrefix );
  }
  if( m_Mirror.Switches.linkerNeedsLibExtension != m_Switches.linkerNeedsLibExtension ) {
    cfg->Write( tmp + _T( "/switches/linkerNeedsLibExtension" ), m_Switches.linkerNeedsLibExtension );
  }
  if( m_Mirror.Switches.linkerNeedsPathResolved != m_Switches.linkerNeedsPathResolved ) {
    cfg->Write( tmp + _T( "/switches/linkerNeedsPathResolved" ), m_Switches.linkerNeedsPathResolved );
  }
  if( m_Mirror.Switches.forceFwdSlashes != m_Switches.forceFwdSlashes ) {
    cfg->Write( tmp + _T( "/switches/forceFwdSlashes" ),   m_Switches.forceFwdSlashes );
  }
  if( m_Mirror.Switches.supportsPCH != m_Switches.supportsPCH ) {
    cfg->Write( tmp + _T( "/switches/supportsPCH" ),  m_Switches.supportsPCH );
  }
  if( m_Mirror.Switches.PCHExtension != m_Switches.PCHExtension ) {
    cfg->Write( tmp + _T( "/switches/pchExtension" ), m_Switches.PCHExtension );
  }
  if( m_Mirror.Switches.UseFlatObjects != m_Switches.UseFlatObjects ) {
    cfg->Write( tmp + _T( "/switches/UseFlatObjects" ), m_Switches.UseFlatObjects );
  }
  if( m_Mirror.Switches.UseFullSourcePaths != m_Switches.UseFullSourcePaths ) {
    cfg->Write( tmp + _T( "/switches/UseFullSourcePaths" ), m_Switches.UseFullSourcePaths );
  }
  if( m_Mirror.Switches.includeDirSeparator != m_Switches.includeDirSeparator ) {
    cfg->Write( tmp + _T( "/switches/includeDirSeparator" ), ( int )m_Switches.includeDirSeparator );
  }
  if( m_Mirror.Switches.libDirSeparator != m_Switches.libDirSeparator ) {
    cfg->Write( tmp + _T( "/switches/libDirSeparator" ), ( int )m_Switches.libDirSeparator );
  }
  if( m_Mirror.Switches.objectSeparator != m_Switches.objectSeparator ) {
    cfg->Write( tmp + _T( "/switches/objectSeparator" ), ( int )m_Switches.objectSeparator );
  }
  if( m_Mirror.Switches.statusSuccess != m_Switches.statusSuccess ) {
    cfg->Write( tmp + _T( "/switches/statusSuccess" ), m_Switches.statusSuccess );
  }
  cfg->DeleteSubPath( tmp + _T( "/regex" ) );
  wxString group;
  for( size_t i = 0; i < m_RegExes.size(); ++i ) {
    if( i < m_Mirror.RegExes.size() && m_Mirror.RegExes[i] == m_RegExes[i] ) {
      continue;
    }
    group.Printf( _T( "%s/regex/re%3.3lu" ), tmp.c_str(), static_cast<unsigned long>( i + 1 ) );
    RegExStruct& rs = m_RegExes[i];
    cfg->Write( group + _T( "/description" ), rs.desc, true );
    if( rs.lt != 0 ) {
      cfg->Write( group + _T( "/type" ), rs.lt );
    }
    cfg->Write( group + _T( "/regex" ),  rs.GetRegExString(), true );
    if( rs.msg[0] != 0 ) {
      cfg->Write( group + _T( "/msg1" ), rs.msg[0] );
    }
    if( rs.msg[1] != 0 ) {
      cfg->Write( group + _T( "/msg2" ), rs.msg[1] );
    }
    if( rs.msg[2] != 0 ) {
      cfg->Write( group + _T( "/msg3" ), rs.msg[2] );
    }
    if( rs.filename != 0 ) {
      cfg->Write( group + _T( "/filename" ), rs.filename );
    }
    if( rs.line != 0 ) {
      cfg->Write( group + _T( "/line" ), rs.line );
    }
  }
  if( m_Mirror.SortOptions[0] != GetCOnlyFlags() ) {
    cfg->Write( tmp + _T( "/sort/C" ), GetCOnlyFlags() );
  }
  if( m_Mirror.SortOptions[1] != GetCPPOnlyFlags() ) {
    cfg->Write( tmp + _T( "/sort/CPP" ), GetCPPOnlyFlags() );
  }
  wxString configpath = tmp + _T( "/custom_variables/" );
  cfg->DeleteSubPath( configpath );
  const StringHash& v = GetAllVars();
  for( StringHash::const_iterator it = v.begin(); it != v.end(); ++it ) {
    cfg->Write( configpath + it->first, it->second );
  }
}

void Compiler::LoadSettings( const wxString& baseKey ) {
  MirrorCurrentSettings();
  ConfigManager* cfg = Manager::Get()->GetConfigManager( _T( "compiler" ) );
  wxString tmp;
  static bool saidAboutCompilerIDs = false;
  tmp.Printf( _T( "%s/set%3.3d" ), baseKey.c_str(), CompilerFactory::GetCompilerIndex( this ) + 1 );
  if( cfg->Exists( tmp + _T( "/name" ) ) ) {
    if( !saidAboutCompilerIDs ) {
      saidAboutCompilerIDs = true;
      InfoWindow::Tip( _( "编译器现在使用唯一的名称而不是ID 项目将做相应自动更新" ), _( "提示" ), 2500 );
    }
  } else {
    tmp.Printf( _T( "%s/%s" ), baseKey.c_str(), m_ID.c_str() );
  }
  if( !cfg->Exists( tmp + _T( "/name" ) ) ) {
    tmp.Replace( _T( "-" ), wxEmptyString );
    if( !cfg->Exists( tmp + _T( "/name" ) ) ) {
      return;
    }
  }
  wxString sep = wxFileName::GetPathSeparator();
  m_Name = cfg->Read( tmp + _T( "/name" ), m_Name );
  m_MasterPath = cfg->Read( tmp + _T( "/master_path" ), m_MasterPath );
  m_ExtraPaths = MakeUniqueArray( GetArrayFromString( cfg->Read( tmp + _T( "/extra_paths" ), _T( "" ) ), _T( ";" ) ), true );
  m_Programs.C = cfg->Read( tmp + _T( "/c_compiler" ), m_Programs.C );
  m_Programs.CPP = cfg->Read( tmp + _T( "/cpp_compiler" ), m_Programs.CPP );
  m_Programs.LD = cfg->Read( tmp + _T( "/linker" ), m_Programs.LD );
  m_Programs.LIB = cfg->Read( tmp + _T( "/lib_linker" ), m_Programs.LIB );
  m_Programs.MAKE = cfg->Read( tmp + _T( "/make" ), m_Programs.MAKE );
  m_Programs.DBGconfig = cfg->Read( tmp + _T( "/debugger_config" ), m_Programs.DBGconfig );
  SetCompilerOptions( GetArrayFromString( cfg->Read( tmp + _T( "/compiler_options" ), wxEmptyString ) ) );
  SetLinkerOptions( GetArrayFromString( cfg->Read( tmp + _T( "/linker_options" ), wxEmptyString ) ) );
  SetIncludeDirs( GetArrayFromString( cfg->Read( tmp + _T( "/include_dirs" ), wxEmptyString ) ) );
  SetLibDirs( GetArrayFromString( cfg->Read( tmp + _T( "/library_dirs" ), wxEmptyString ) ) );
  SetCommandsBeforeBuild( GetArrayFromString( cfg->Read( tmp + _T( "/commands_before" ), wxEmptyString ) ) );
  SetCommandsAfterBuild( GetArrayFromString( cfg->Read( tmp + _T( "/commands_after" ), wxEmptyString ) ) );
  for( int i = 0; i < ctCount; ++i ) {
    wxArrayString keys = cfg->EnumerateSubPaths( tmp + _T( "/macros/" ) + CommandTypeDescriptions[i] );
    for( size_t n = 0; n < keys.size(); ++n ) {
      unsigned long index = 0;
      if( keys[n].Mid( 4 ).ToULong( &index ) ) {
        while( index >= m_Commands[i].size() ) {
          m_Commands[i].push_back( CompilerTool() );
        }
        CompilerTool& tool = m_Commands[i][index];
        wxString key = F( _T( "%s/macros/%s/tool%lu/" ), tmp.c_str(), CommandTypeDescriptions[i].c_str(), index );
        tool.command = cfg->Read( key + _T( "command" ) );
        tool.extensions = cfg->ReadArrayString( key + _T( "extensions" ) );
        tool.generatedFiles = cfg->ReadArrayString( key + _T( "generatedFiles" ) );
      }
    }
  }
  m_Switches.includeDirs = cfg->Read( tmp + _T( "/switches/includes" ), m_Switches.includeDirs );
  m_Switches.libDirs = cfg->Read( tmp + _T( "/switches/libs" ), m_Switches.libDirs );
  m_Switches.linkLibs = cfg->Read( tmp + _T( "/switches/link" ), m_Switches.linkLibs );
  m_Switches.defines = cfg->Read( tmp + _T( "/switches/define" ), m_Switches.defines );
  m_Switches.genericSwitch = cfg->Read( tmp + _T( "/switches/generic" ), m_Switches.genericSwitch );
  m_Switches.objectExtension = cfg->Read( tmp + _T( "/switches/objectext" ), m_Switches.objectExtension );
  m_Switches.needDependencies = cfg->ReadBool( tmp + _T( "/switches/deps" ), m_Switches.needDependencies );
  m_Switches.forceCompilerUseQuotes  = cfg->ReadBool( tmp + _T( "/switches/forceCompilerQuotes" ), m_Switches.forceCompilerUseQuotes );
  m_Switches.forceLinkerUseQuotes = cfg->ReadBool( tmp + _T( "/switches/forceLinkerQuotes" ), m_Switches.forceLinkerUseQuotes );
  m_Switches.logging = ( CompilerLoggingType )cfg->ReadInt( tmp + _T( "/switches/logging" ), m_Switches.logging );
  m_Switches.libPrefix = cfg->Read( tmp + _T( "/switches/libPrefix" ), m_Switches.libPrefix );
  m_Switches.libExtension = cfg->Read( tmp + _T( "/switches/libExtension" ), m_Switches.libExtension );
  m_Switches.linkerNeedsLibPrefix = cfg->ReadBool( tmp + _T( "/switches/linkerNeedsLibPrefix" ), m_Switches.linkerNeedsLibPrefix );
  m_Switches.linkerNeedsLibExtension = cfg->ReadBool( tmp + _T( "/switches/linkerNeedsLibExtension" ), m_Switches.linkerNeedsLibExtension );
  m_Switches.linkerNeedsPathResolved = cfg->ReadBool( tmp + _T( "/switches/linkerNeedsPathResolved" ), m_Switches.linkerNeedsPathResolved );
  m_Switches.forceFwdSlashes = cfg->ReadBool( tmp + _T( "/switches/forceFwdSlashes" ),   m_Switches.forceFwdSlashes );
  m_Switches.supportsPCH = cfg->ReadBool( tmp + _T( "/switches/supportsPCH" ),  m_Switches.supportsPCH );
  m_Switches.PCHExtension = cfg->Read( tmp + _T( "/switches/pchExtension" ), m_Switches.PCHExtension );
  m_Switches.UseFlatObjects = cfg->ReadBool( tmp + _T( "/switches/UseFlatObjects" ), m_Switches.UseFlatObjects );
  m_Switches.UseFullSourcePaths = cfg->ReadBool( tmp + _T( "/switches/UseFullSourcePaths" ), m_Switches.UseFullSourcePaths );
  m_Switches.includeDirSeparator = ( wxChar )cfg->ReadInt( tmp + _T( "/switches/includeDirSeparator" ), ( int )m_Switches.includeDirSeparator );
  m_Switches.libDirSeparator = ( wxChar )cfg->ReadInt( tmp + _T( "/switches/libDirSeparator" ), ( int )m_Switches.libDirSeparator );
  m_Switches.objectSeparator = ( wxChar )cfg->ReadInt( tmp + _T( "/switches/objectSeparator" ), ( int )m_Switches.objectSeparator );
  m_Switches.statusSuccess = cfg->ReadInt( tmp + _T( "/switches/statusSuccess" ), m_Switches.statusSuccess );
  wxArrayString keys = cfg->EnumerateSubPaths( tmp + _T( "/regex/" ) );
  wxString group;
  long index = 0;
  for( size_t i = 0; i < keys.GetCount(); ++i ) {
    wxString key = keys[i];
    if( !key.StartsWith( _T( "re" ) ) ) {
      continue;
    }
    key.Remove( 0, 2 );
    if( !key.ToLong( &index, 10 ) ) {
      continue;
    }
    group.Printf( _T( "%s/regex/re%3.3ld" ), tmp.c_str(), index );
    if( !cfg->Exists( group + _T( "/description" ) ) ) {
      continue;
    }
    RegExStruct rs( cfg->Read( group + _T( "/description" ) ),
                    ( CompilerLineType )cfg->ReadInt( group + _T( "/type" ), 0 ),
                    cfg->Read( group + _T( "/regex" ) ),
                    cfg->ReadInt( group + _T( "/msg1" ), 0 ),
                    cfg->ReadInt( group + _T( "/filename" ), 0 ),
                    cfg->ReadInt( group + _T( "/line" ), 0 ),
                    cfg->ReadInt( group + _T( "/msg2" ), 0 ),
                    cfg->ReadInt( group + _T( "/msg3" ), 0 ) );
    if( index <= ( long )m_RegExes.size() ) {
      m_RegExes[index - 1] = rs;
    } else
    { m_RegExes.push_back( rs ); }
  }
  m_SortOptions[0] = cfg->Read( tmp + _T( "/sort/C" ), m_SortOptions[0] );
  m_SortOptions[1] = cfg->Read( tmp + _T( "/sort/CPP" ), m_SortOptions[1] );
  wxString configpath = tmp + _T( "/custom_variables/" );
  UnsetAllVars();
  wxArrayString list = cfg->EnumerateKeys( configpath );
  for( unsigned int i = 0; i < list.GetCount(); ++i ) {
    SetVar( list[i], cfg->Read( configpath + _T( '/' ) + list[i] ), false );
  }
}

CompilerLineType Compiler::CheckForWarningsAndErrors( const wxString& line ) {
  if( !m_MultiLineMessages || ( m_MultiLineMessages && !m_Error.IsEmpty() ) ) {
    m_ErrorFilename.Clear();
    m_ErrorLine.Clear();
    m_Error.Clear();
  }
  for( size_t i = 0; i < m_RegExes.size(); ++i ) {
    RegExStruct& rs = m_RegExes[i];
    if( !rs.HasRegEx() ) {
      continue;
    }
    const wxRegEx &regex = rs.GetRegEx();
    if( regex.Matches( line ) ) {
      if( rs.filename > 0 ) {
        m_ErrorFilename = UnixFilename( regex.GetMatch( line, rs.filename ) );
      }
      if( rs.line > 0 ) {
        m_ErrorLine = regex.GetMatch( line, rs.line );
      }
      for( int x = 0; x < 3; ++x ) {
        if( rs.msg[x] > 0 ) {
          if( !m_Error.IsEmpty() ) {
            m_Error << _T( " " );
          }
          m_Error << regex.GetMatch( line, rs.msg[x] );
        }
      }
      return rs.lt;
    }
  }
  return cltNormal;
}

void Compiler::LoadDefaultOptions( const wxString& name, int recursion ) {
  wxXmlDocument options;
  wxString msg;
  wxString doc = ConfigManager::LocateDataFile( _T( "compilers/options_" ) + name + _T( ".xml" ), sdDataUser | sdDataGlobal );
  if( doc.IsEmpty() ) {
    msg << _T( "错误: 文件 'options_" ) + name + _T( ".xml' 找不到." ) ;
    InfoWindow::Tip( msg, _T( "错误" ), 3500 );
    return;
  }
  if( recursion > 5 ) {
    msg << _T( "警告: '" ) + doc + _T( "' 由于过度递归而未加载." ) ;
    InfoWindow::Tip( msg, _T( "错误" ), 3500 );
    return;
  }
  if( !options.Load( doc ) ) {
    msg << _T( "错误: 编译器选项文件 '" ) + doc + _T( "' 未找到编译器 '" ) + name + _T( "'." ) ;
    InfoWindow::Tip( msg, _T( "错误" ), 3500 );
    return;
  }
  if( options.GetRoot()->GetName() != _T( "m_Code_compiler_options" ) ) {
    msg << _T( "错误: 编译器选项无效 '" ) + name + _T( "'." ) ;
    InfoWindow::Tip( msg, _T( "错误" ), 3500 );
    return;
  }
  wxString extends = options.GetRoot()->GetAttribute( _T( "extends" ), wxEmptyString );
  if( !extends.IsEmpty() ) {
    LoadDefaultOptions( extends, recursion + 1 );
  }
  wxXmlNode* node = options.GetRoot()->GetChildren();
  int depth = 0;
  wxString categ;
  bool exclu = false;
  wxString baseKey = GetParentID().IsEmpty() ? _T( "/sets" ) : _T( "/user_sets" );
  ConfigManager* cfg = Manager::Get()->GetConfigManager( _T( "compiler" ) );
  wxString cmpKey;
  cmpKey.Printf( _T( "%s/set%3.3d" ), baseKey.c_str(), CompilerFactory::GetCompilerIndex( this ) + 1 );
  if( !cfg->Exists( cmpKey + _T( "/name" ) ) ) {
    cmpKey.Printf( _T( "%s/%s" ), baseKey.c_str(), m_ID.c_str() );
  }
  if( !cfg->Exists( cmpKey + _T( "/name" ) ) ) {
    cmpKey.Replace( _T( "-" ), wxEmptyString );
  }
  while( node ) {
    const wxString value = node->GetAttribute( _T( "value" ), wxEmptyString );
    if( node->GetName() == _T( "if" ) && node->GetChildren() ) {
      if( EvalXMLCondition( node ) ) {
        node = node->GetChildren();
        ++depth;
        continue;
      } else if( node->GetNext() && node->GetNext()->GetName() == _T( "else" ) &&
                 node->GetNext()->GetChildren() ) {
        node = node->GetNext()->GetChildren();
        ++depth;
        continue;
      }
    } else if( node->GetName() == _T( "Program" ) ) {
      wxString prog = node->GetAttribute( _T( "name" ), wxEmptyString );
      if( prog == _T( "C" ) ) {
        m_Programs.C = cfg->Read( cmpKey + _T( "/c_compiler" ), value );
        m_Mirror.Programs.C = value;
      } else if( prog == _T( "CPP" ) ) {
        m_Programs.CPP = cfg->Read( cmpKey + _T( "/cpp_compiler" ), value );
        m_Mirror.Programs.CPP = value;
      } else if( prog == _T( "LD" ) ) {
        m_Programs.LD = cfg->Read( cmpKey + _T( "/linker" ), value );
        m_Mirror.Programs.LD = value;
      } else if( prog == _T( "DBGconfig" ) ) {
        m_Programs.DBGconfig = value;
      } else if( prog == _T( "LIB" ) ) {
        m_Programs.LIB = cfg->Read( cmpKey + _T( "/lib_linker" ), value );
        m_Mirror.Programs.LIB = value;
      } else if( prog == _T( "MAKE" ) ) {
        m_Programs.MAKE = cfg->Read( cmpKey + _T( "/make" ), value );
        m_Mirror.Programs.MAKE = value;
      }
    } else if( node->GetName() == _T( "Switch" ) ) {
      wxString swi = node->GetAttribute( _T( "name" ), wxEmptyString );
      if( swi == _T( "includeDirs" ) ) {
        m_Switches.includeDirs = value;
      } else if( swi == _T( "libDirs" ) ) {
        m_Switches.libDirs = value;
      } else if( swi == _T( "linkLibs" ) ) {
        m_Switches.linkLibs = value;
      } else if( swi == _T( "defines" ) ) {
        m_Switches.defines = value;
      } else if( swi == _T( "genericSwitch" ) ) {
        m_Switches.genericSwitch = value;
      } else if( swi == _T( "objectExtension" ) ) {
        m_Switches.objectExtension = value;
      } else if( swi == _T( "forceFwdSlashes" ) ) {
        m_Switches.forceFwdSlashes = ( value == _T( "true" ) );
      } else if( swi == _T( "forceLinkerUseQuotes" ) ) {
        m_Switches.forceLinkerUseQuotes = ( value == _T( "true" ) );
      } else if( swi == _T( "forceCompilerUseQuotes" ) ) {
        m_Switches.forceCompilerUseQuotes = ( value == _T( "true" ) );
      } else if( swi == _T( "needDependencies" ) ) {
        m_Switches.needDependencies = ( value == _T( "true" ) );
      } else if( swi == _T( "logging" ) ) {
        if( value == _T( "full" ) ) {
          m_Switches.logging = clogFull;
        } else if( value == _T( "simple" ) ) {
          m_Switches.logging = clogSimple;
        } else if( value == _T( "none" ) ) {
          m_Switches.logging = clogNone;
        } else
        { m_Switches.logging = CompilerSwitches::defaultLogging; }
      } else if( swi == _T( "libPrefix" ) ) {
        m_Switches.libPrefix = value;
      } else if( swi == _T( "libExtension" ) ) {
        m_Switches.libExtension = value;
      } else if( swi == _T( "linkerNeedsLibPrefix" ) ) {
        m_Switches.linkerNeedsLibPrefix = ( value == _T( "true" ) );
      } else if( swi == _T( "linkerNeedsLibExtension" ) ) {
        m_Switches.linkerNeedsLibExtension = ( value == _T( "true" ) );
      } else if( swi == _T( "linkerNeedsPathResolved" ) ) {
        m_Switches.linkerNeedsPathResolved = ( value == _T( "true" ) );
      } else if( swi == _T( "supportsPCH" ) ) {
        m_Switches.supportsPCH = ( value == _T( "true" ) );
      } else if( swi == _T( "PCHExtension" ) ) {
        m_Switches.PCHExtension = value;
      } else if( swi == _T( "UseFlatObjects" ) ) {
        m_Switches.UseFlatObjects = ( value == _T( "true" ) );
      } else if( swi == _T( "UseFullSourcePaths" ) ) {
        m_Switches.UseFullSourcePaths = ( value == _T( "true" ) );
      } else if( swi == _T( "includeDirSeparator" ) && !value.IsEmpty() ) {
        m_Switches.includeDirSeparator = value[0];
      } else if( swi == _T( "libDirSeparator" ) && !value.IsEmpty() ) {
        m_Switches.libDirSeparator = value[0];
      } else if( swi == _T( "objectSeparator" ) && !value.IsEmpty() ) {
        m_Switches.objectSeparator = value[0];
      } else if( swi == _T( "statusSuccess" ) && !value.IsEmpty() ) {
        long val;
        if( value.ToLong( &val ) ) {
          m_Switches.statusSuccess = val;
        }
      }
    } else if( node->GetName() == _T( "Category" ) && node->GetChildren() ) {
      categ = node->GetAttribute( _T( "name" ), wxEmptyString );
      exclu = ( node->GetAttribute( _T( "exclusive" ), wxEmptyString ) == _T( "true" ) );
      node = node->GetChildren();
      ++depth;
      continue;
    } else if( node->GetName() == _T( "Option" ) ) {
      wxString category;
      if( !node->GetAttribute( _T( "category" ), &category ) ) {
        if( categ.IsEmpty() ) {
          category = _T( "General" );
        } else {
          category = categ;
        }
      }
      wxString exclusive;
      if( !node->GetAttribute( _T( "exclusive" ), &exclusive ) ) {
        exclusive = ( exclu ? _T( "true" ) : _T( "false" ) );
      }
      m_Options.AddOption( wxGetTranslation( node->GetAttribute( _T( "name" ), wxEmptyString ) ),
                           node->GetAttribute( _T( "option" ), wxEmptyString ),
                           wxGetTranslation( category ),
                           node->GetAttribute( _T( "additionalLibs" ), wxEmptyString ),
                           node->GetAttribute( _T( "checkAgainst" ), wxEmptyString ),
                           wxGetTranslation( node->GetAttribute( _T( "checkMessage" ), wxEmptyString ) ),
                           node->GetAttribute( _T( "supersedes" ), wxEmptyString ),
                           exclusive == _T( "true" ) );
    } else if( node->GetName() == _T( "Command" ) ) {
      wxString cmd = node->GetAttribute( _T( "name" ), wxEmptyString );
      wxString unEscape = value;
      unEscape.Replace( _T( "\\n" ), _T( "\n" ) );
      CompilerTool tool( unEscape, node->GetAttribute( _T( "ext" ), wxEmptyString ),
                         node->GetAttribute( _T( "gen" ), wxEmptyString ) );
      CommandType cmdTp = ctCount;
      if( cmd == _T( "CompileObject" ) ) {
        cmdTp = ctCompileObjectCmd;
      } else if( cmd == _T( "GenDependencies" ) ) {
        cmdTp = ctGenDependenciesCmd;
      } else if( cmd == _T( "CompileResource" ) ) {
        cmdTp = ctCompileResourceCmd;
      } else if( cmd == _T( "LinkExe" ) ) {
        cmdTp = ctLinkExeCmd;
      } else if( cmd == _T( "LinkConsoleExe" ) ) {
        cmdTp = ctLinkConsoleExeCmd;
      } else if( cmd == _T( "LinkDynamic" ) ) {
        cmdTp = ctLinkDynamicCmd;
      } else if( cmd == _T( "LinkStatic" ) ) {
        cmdTp = ctLinkStaticCmd;
      } else if( cmd == _T( "LinkNative" ) ) {
        cmdTp = ctLinkNativeCmd;
      }
      if( cmdTp != ctCount ) {
        bool assigned = false;
        CompilerToolsVector& tools = m_Commands[cmdTp];
        for( size_t i = 0; i < tools.size(); ++i ) {
          if( tools[i].extensions == tool.extensions ) {
            tools[i] = tool;
            assigned = true;
            break;
          }
        }
        if( !assigned ) {
          tools.push_back( tool );
        }
      }
    } else if( node->GetName() == _T( "Sort" ) ) {
      wxString flags;
      if( node->GetAttribute( _T( "CFlags" ), &flags ) ) {
        flags.Replace( _T( "\n" ), _T( " " ) );
        flags.Replace( _T( "\r" ), _T( " " ) );
        SetCOnlyFlags( MakeUniqueString( GetCOnlyFlags() + _T( " " ) + flags, _T( " " ) ) );
      } else if( node->GetAttribute( _T( "CPPFlags" ), &flags ) ) {
        flags.Replace( _T( "\n" ), _T( " " ) );
        flags.Replace( _T( "\r" ), _T( " " ) );
        SetCPPOnlyFlags( MakeUniqueString( GetCPPOnlyFlags() + _T( " " ) + flags, _T( " " ) ) );
      }
    } else if( node->GetName() == _T( "Common" ) ) {
      LoadDefaultOptions( _T( "common_" ) + node->GetAttribute( _T( "name" ), wxEmptyString ), recursion + 1 );
    }
    while( !node->GetNext() && depth > 0 ) {
      node = node->GetParent();
      if( node->GetName() == _T( "Category" ) ) {
        categ = wxEmptyString;
        exclu = false;
      }
      --depth;
    }
    node = node->GetNext();
  }
  if( recursion == 0 ) {
    m_Programs.C  = m_Mirror.Programs.C;
    m_Programs.CPP = m_Mirror.Programs.CPP;
    m_Programs.LD = m_Mirror.Programs.LD;
    m_Programs.LIB = m_Mirror.Programs.LIB;
    m_Programs.MAKE = m_Mirror.Programs.MAKE;
  }
}

void Compiler::LoadRegExArray( const wxString& name, bool globalPrecedence, int recursion ) {
  wxXmlDocument options;
  wxString doc;
  const wxString fn = _T( "compilers/options_" ) + name + _T( ".xml" );
  if( globalPrecedence ) {
    doc = ConfigManager::LocateDataFile( fn, sdDataGlobal );
    if( doc.IsEmpty() ) {
      doc = ConfigManager::LocateDataFile( fn, sdDataUser );
    }
  } else {
    doc = ConfigManager::LocateDataFile( fn, sdDataUser | sdDataGlobal );
  }
  if( doc.IsEmpty() ) {
    Manager::Get()->GetLogManager()->Log( _T( "错误: 文件 'options_" ) + name + _T( ".xml' 找不到" ) );
    return;
  }
  if( recursion > 5 ) {
    Manager::Get()->GetLogManager()->LogWarning( _T( "警告: '" ) + doc + _T( "' 由于过度递归而未加载" ) );
    return;
  }
  if( !options.Load( doc ) ) {
    Manager::Get()->GetLogManager()->Log( _T( "错误分析 " ) + doc );
    return;
  }
  wxString extends = options.GetRoot()->GetAttribute( _T( "extends" ), wxEmptyString );
  if( !extends.IsEmpty() ) {
    LoadRegExArray( extends, globalPrecedence, recursion + 1 );
  }
  wxXmlNode* node = options.GetRoot()->GetChildren();
  int depth = 0;
  while( node ) {
    const wxString value = node->GetAttribute( _T( "value" ), wxEmptyString );
    if( node->GetName() == _T( "if" ) && node->GetChildren() ) {
      if( EvalXMLCondition( node ) ) {
        node = node->GetChildren();
        ++depth;
        continue;
      } else if( node->GetNext() && node->GetNext()->GetName() == _T( "else" ) &&
                 node->GetNext()->GetChildren() ) {
        node = node->GetNext()->GetChildren();
        ++depth;
        continue;
      }
    } else if( node->GetName() == _T( "RegEx" ) ) {
      wxString tp = node->GetAttribute( _T( "type" ), wxEmptyString );
      CompilerLineType clt = cltNormal;
      if( tp == _T( "warning" ) ) {
        clt = cltWarning;
      } else if( tp == _T( "error" ) ) {
        clt = cltError;
      } else if( tp == _T( "info" ) ) {
        clt = cltInfo;
      }
      wxArrayString msg = GetArrayFromString( node->GetAttribute( _T( "msg" ), wxEmptyString ) + _T( ";0;0" ) );
      m_RegExes.push_back( RegExStruct( wxGetTranslation( node->GetAttribute( _T( "name" ), wxEmptyString ) ), clt,
                                        node->GetNodeContent().Trim().Trim( false ), wxAtoi( msg[0] ),
                                        wxAtoi( node->GetAttribute( _T( "file" ), _T( "0" ) ) ),
                                        wxAtoi( node->GetAttribute( _T( "line" ), _T( "0" ) ) ),
                                        wxAtoi( msg[1] ), wxAtoi( msg[2] ) ) );
    } else if( node->GetName() == _T( "Common" ) ) {
      LoadRegExArray( _T( "common_" ) + node->GetAttribute( _T( "name" ), wxEmptyString ),
                      globalPrecedence, recursion + 1 );
    }
    while( !node->GetNext() && depth > 0 ) {
      node = node->GetParent();
      --depth;
    }
    node = node->GetNext();
  }
}

bool Compiler::EvalXMLCondition( const wxXmlNode* node ) {
  bool val = true;
  wxString test;
  if( node->GetAttribute( _T( "exec" ), &test ) ) {
    wxArrayString cmd = GetArrayFromString( test, _T( " " ) );
    if( cmd.IsEmpty() ) {
      return false;
    }
    wxString path;
    wxGetEnv( _T( "PATH" ), &path );
    const wxString origPath = path;
    ConfigManager* cfg = Manager::Get()->GetConfigManager( _T( "compiler" ) );
    wxString masterPath;
    wxString loc = ( m_ParentID.IsEmpty() ? _T( "/sets/" ) : _T( "/user_sets/" ) ) + m_ID;
    wxArrayString extraPaths;
    if( cfg->Exists( loc + _T( "/name" ) ) ) {
      masterPath = cfg->Read( loc + _T( "/master_path" ), wxEmptyString );
      extraPaths = MakeUniqueArray( GetArrayFromString( cfg->Read( loc + _T( "/extra_paths" ), wxEmptyString ) ), true );
    }
    for( size_t i = 0; i < extraPaths.GetCount(); ++i ) {
      path.Prepend( extraPaths[i] + wxPATH_SEP );
    }
    if( !masterPath.IsEmpty() ) {
      path.Prepend( masterPath + wxPATH_SEP + masterPath + wxFILE_SEP_PATH + _T( "bin" ) + wxPATH_SEP );
    }
    wxSetEnv( _T( "PATH" ), path );
    cmd[0] = GetExecName( cmd[0] );
    long ret = -1;
    if( !cmd[0].IsEmpty() ) {
      int flags = wxEXEC_SYNC;
      flags |= wxEXEC_NODISABLE;
      wxLogNull logNo;
      ret = wxExecute( GetStringFromArray( cmd, _T( " " ), false ), cmd, flags );
    }
    if( ret != 0 ) {
      val = ( node->GetAttribute( _T( "default" ), wxEmptyString ) == _T( "true" ) );
    } else if( node->GetAttribute( _T( "regex" ), &test ) ) {
      wxRegEx re;
      if( re.Compile( test ) ) {
        for( size_t i = 0; i < cmd.GetCount(); ++i ) {
          if( re.Matches( cmd[i] ) ) {
            val = true;
            break;
          }
        }
      }
    } else
    { wxSetEnv( _T( "PATH" ), origPath ); }
  }
  return val;
}

wxString Compiler::GetExecName( const wxString& name ) {
  wxString ret = name;
  if( name == _T( "C" ) ) {
    ret = m_Programs.C;
  } else if( name == _T( "CPP" ) ) {
    ret = m_Programs.CPP;
  } else if( name == _T( "LD" ) ) {
    ret = m_Programs.LD;
  } else if( name == _T( "LIB" ) ) {
    ret = m_Programs.LIB;
  } else if( name == _T( "MAKE" ) ) {
    ret = m_Programs.MAKE;
  }
  return ret;
}
